/*
 * @Author: zhengshundong
 * @Date:   2020-08-26 20:30:47
 * @Last Modified by:   zhengshundong
 * @Last Modified time: 2020-08-26 20:47:21
 */
/**
 * #info
 *
 *@author   wuwg
 *@createTime   2018/03/20
 *@updateTime   2018/03/21
 *@description  select  component  下拉组件
 */
/**
 * #item
 *
 * @type        aty-select
 * @cn            下拉列表
 * @nameprop     label
 * @menu        表单
 * @stPlugin    true
 * @stContainer false
 * @children    [aty-option,aty-option-group]
 * @mixins      [./components/components/form/form-item/form-item.js]
 */
// 扩展属性
/**
 * #extendproperty
 *
 * @name    slot
 * @cn      插槽
 * @type    list
 * @options [input,option,optionGroup]
 * @description  input：<aty-panel  slot="input" >input</aty-panel>
 * option：<aty-option  slot="option" v-for="(item,index) in cityList"
 * :key="item.code" :value="item.code"  :disabled="item.disabled":label="item.name">
 * <aty-text   class="fd-index"  v-text="index+1"></aty-text>
 * <aty-text   v-text="item.name"></aty-text>
 * <aty-text  class="fd-key"   v-text="item.code"></aty-text>
 * </aty-option>
 * optionGroup：<aty-option-group  slot="optionGroup"   :data="cityList3"></aty-option-group>
 * <aty-option-group  slot="optionGroup"   :data="cityList3">
 * <template slot-scope="props" >
 * <aty-option  v-for="(item,index) in props.item"
 * :key="item.code"
 * :value="item.code"
 * :disabled="item.disabled"
 * :label="item.name">
 * <aty-text   class="fd-index"  v-text="index+1"></aty-text>
 * <aty-text  v-text="item.name"></aty-text>
 * <aty-text  class="fd-key"   v-text="item.code"></aty-text>
 * </aty-option>
 * </template>
 * </aty-option-group>
 */
import config from '../../../config/config.js'

// components
import Drop from '../../form/select/dropdown/index.vue'
import atyOptionGroup from './option-group/index.vue'
import atyOption from './option/index.vue'
// directives
import clickoutside from '../../../directives/clickoutside'
import TransferDom from '../../../directives/transfer-dom'
import Emitter from '../../../mixins/emitter.js'
import formItem from '../form-item/form-item.js'
// mixins
import {
  t
} from '../../../locale/index.js'
import {
  debounce
} from './utils/utils.js'
// prefix
const prefixCls = config.prefix + '-select'
// module
export default {
  name: prefixCls,
  mixins: [Emitter, formItem],
  components: {
    Drop,
    atyOptionGroup,
    atyOption
  },
  directives: {
    clickoutside,
    TransferDom
  },
  props: {
    /**
     * #property
     *
     * @name     name
     * @cn      属性名称
     * @group   基本属性
     * @type    string
     * @description 值对应的属性名
     */
    name: {
      type: String
    },
    /**
     * #property
     *
     * @name    value
     * @cn      值
     * @group   基本属性
     * @type    string,number,array
     * @description input的值
     */
    value: {
      type: [String, Number, Array],
      default: ''
    },
    /**
     * #property
     *
     * @name    multiple
     * @cn      是否支持多选
     * @group   控制
     * @type    boolean
     * @value  false
     * @description 是否支持多选
     */
    multiple: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    disabled
     * @cn      是否禁用
     * @group   控制
     * @type    boolean
     * @value  false
     * @description 该下拉框是否禁用
     */
    disabled: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    clearable
     * @cn      是否有清除按钮
     * @group   控制
     * @type    boolean
     * @value  false
     * @description 该下拉框是否有清除按钮
     */
    clearable: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    placeholder
     * @cn      占位文本
     * @group   基本属性
     * @type    string
     * @value   请选择
     * @description 占位文本
     */
    placeholder: {
      type: String,
      default () {
        return t('i.select.placeholder')
      }
    },
    /**
     * #property
     *
     * @name    loadingText
     * @cn      远程搜索中的文字提示
     * @group   基本属性
     * @type    string
     * @value   加载中...
     * @description 远程搜索中的文字提示；
     */
    loadingText: {
      type: String,
      default () {
        return t('i.select.loading')
      }
    },
    /**
     * #property
     *
     * @name    labelInValue
     * @cn      在返回选项时，是否将 label 和 value 一并返回，默认只返回 value
     * @group   控制
     * @type    boolean
     * @value  false
     * @description 在返回选项时，是否将 label 和 value 一并返回，默认只返回 value；
     */
    labelInValue: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    notFoundText
     * @cn      当下拉列表为空时显示的内容
     * @group   基本属性
     * @type    string
     * @value   无匹配数据
     * @description 当下拉列表为空时显示的内容；
     */
    notFoundText: {
      type: String,
      default () {
        return t('i.select.noMatch')
      }
    },
    /**
     * #property
     *
     * @name    placement
     * @cn      下拉框出现的位置
     * @group   样式
     * @type    list
     * @options [top, bottom]
     * @value  bottom
     * @description 该下拉框出现的位置，取值为 ['top', 'bottom']中的其中一个
     */
    placement: {
      validator(value) {
        return Artery.isOneOf(value, ['top', 'bottom'])
      },
      default: 'bottom'
    },
    /**
     * #property
     *
     * @name    transfer
     * @cn      是否将弹层放置于 body 内
     * @group   控制
     * @type    boolean
     * @value  false
     * @description  是否将弹层放置于 body 内，
     *              在 Tabs、带有 fixed 的 Table 列内使用时，建议添加此属性，它将不受父级样式影响，从而达到更好的效果
     */
    transfer: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    elementId
     * @cn      input id
     * @group   基本属性
     * @type    string
     * @description 该输入框上的id
     */
    elementId: {
      type: String
    },
    /**
     * #property
     *
     * @name    showTitle
     * @cn      是否显示title
     * @group   控制
     * @type    boolean
     * @value   false
     * @description 鼠标滑过是否显示title
     */
    showTitle: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    dividedGroups
     * @cn      是否分组
     * @group   控制
     * @type    boolean
     * @value  false
     * @description 该下拉框是否分组
     */
    dividedGroups: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    data
     * @cn      数据列表
     * @group   数据
     * @type    array
     * @bind    must
     * @description 下拉的数据列表,数据格式[{code:'beijing', name:'北京'}， {code:'shanghai', name:'上海'}， {}]
     */
    data: {
      type: Array,
      default () {
        return []
      }
    },
    /**
     * #property
     *
     * @name    codeType
     * @cn      代码类型
     * @group   基本属性
     * @type    codetype
     * @bind    mustnot
     * @value   null
     * @description 代码类型
     */
    codeType: {
      type: String,
      default: null
    },
    /**
     * #property
     *
     * @name    lazy
     * @cn      延迟加载
     * @group   控制
     * @type    boolean
     * @value   false
     * @description 是否延迟加载。此值仅在通过codeType自动加载数据的时候有效。
     */
    lazy: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    dropAsync
     * @cn      下拉菜单异步渲染
     * @group   基本属性
     * @type    boolean
     * @value   false
     * @description 下拉菜单是否异步渲染dom，默认false
     */
    dropAsync: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name     selectLabel
     * @cn      初始化的文本
     * @group   数据
     * @type    string,number,array
     * @description 默认code值对应文本内容,数据列表是异步的情况下，无法通过code获取到对应的name，所以需要手动设置。
     *                单选为字符串，多选为数组
     */
    selectLabel: {
      type: [String, Number, Array],
      default: ''
    },
    /**
     * #property
     *
     * @name    filterable
     * @cn      是否支持搜索
     * @group   控制
     * @type    boolean
     * @bind    must
     * @value   false
     * @description 是否支持搜索，默认是本地搜索
     */
    filterable: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    remoteMethod
     * @cn      远程搜索的方法
     * @group   远程搜索
     * @type    function
     * @bind    must
     * @description 远程搜索的方法；
     *       1.远程“搜索”需同时设置 filterable=true、remote-method=function(){} 两个 props，
     *       remote-method 是远程搜索的方法。
     *       2.当然也可以是以下组合  filterable=true，
     *       3.不需要搜索功能时可以 remote-method=function(){}
     *       如果用户需要设置额外参数可以选择， remoteExtendsParams={}；
     *       ！important特别注意，如果用户数使用 remote-method=function(){}，那么组件将用用户的方法为准。
     */
    remoteMethod: {
      type: Function
    },
    /**
     * #property
     *
     * @name    delay
     * @cn      延迟请求数据的时间
     * @group   控制
     * @type    number
     * @bind    must
     * @value    100
     * @description 延迟请求数据的时间（毫秒）
     */
    delay: {
      type: Number,
      default: 100
    },
    /**
     * #property
     *
     * @name    loading
     * @cn      是否正在加载
     * @group   控制
     * @type    boolean
     * @value  false
     * @description 是否正在加载
     */
    loading: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    dropDownMaxHeight
     * @cn      下拉数据区域最大高度
     * @group   样式
     * @type    number
     * @value   200
     * @bind    must
     * @description 超出最大高度显示滚动条
     */
    dropDownMaxHeight: {
      type: Number,
      default: 200
    },
    /**
     * #property
     *
     * @name    pinyinSearch
     * @cn      拼音搜索
     * @group   控制
     * @type	boolean
     * @value   false
     * @description 是否支持拼音搜索, 需要后台返回的数据对象中含有simpleSpell或fullSpell
     */
    pinyinSearch: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    includeOptionRule
     * @cn      是否包含某元素的规则
     * @group   控制
     * @type    function
     * @bind    must
     * @description 是否包含某元素的规则，用于前端过滤
     */
    includeOptionRule: {
      type: Function
    },
    /**
     * #property
     *
     * @name    dropdownWidth
     * @cn      下拉框宽度
     * @group   样式
     * @type    number
     * @bind    must
     * @description 如不指定和input保持一致, 指定为0则自适应内容宽度
     */
    dropdownWidth: {
      type: Number
    },
    /**
     * #property
     *
     * @name    popupClass
     * @cn      弹出框样式
     * @type    string
     * @description 可自定义弹框的样式或用于定位该元素
     */
    popupClass: {
      type: String
    },
    /**
     * #property
     *
     * @name    showInvalid
     * @cn      显示无效节点
     * @group   控制
     * @type    boolean
     * @value   false
     * @description 设置为true后, 无效的代码值会以disable的形式展现
     */
    showInvalid: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      prefix: config.prefix, // aty前缀 用于其他组件
      prefixCls: prefixCls, // 组件前缀
      dropLoaded: !this.dropAsync,
      currentData: this.data ? this.data : [],
      selected: this.multiple ? [] : '', // 选择的数据，包括label,code等属性
      model: this.multiple ? [] : '', // 当前选择的值
      options: [], //  options组件 列表
      optionInstances: [], //  options 列表实例缓存, 用于改变子组件选中状态
      visible: false, // 是否显示下拉框
      remote: Artery.type(this.remoteMethod) === 'function',
      query: this.multiple ? '' : this.selectLabel,

      currentLoading: this.loading, // 用于loading状态
      focusIndex: 0, // 键盘操作记录
      inputLength: 20, // 动态输入框长度
      notFound: false,
      timerRemote: null
    }
  },
  watch: {
    // 监听loading 变化
    loading(val) {
      this.currentLoading = val
    },
    data: {
      deep: true,
      handler(newValue, oldValue = []) {
        this.setCurrentData(newValue)
        this.focusIndex = 0
        if (this.visible && oldValue.length === 0) { // fixed -> http://code.thunisoft.com/artery/artery-ui/issues/132
          // 异步情况, 数据还没过来就点击了下拉, 这时候位置是不对的, 等数据过来了还需要在更新下
          this.broadcast(config.prefix + '-select-dropdown', 'update-popper')
        }
        // 数据改变更新options实例
        this.updateOptions()
      }
    },
    // 当前的值
    value: {
      handler(val) {
        // #982
        // 这里不能setModel，会触发input
        if (val === '' || val === null || val === undefined) {
          this.model = this.multiple ? [] : ''
        } else {
          this.model = val
        }
        this.$nextTick(() => {
          this.modelToQuery()
          this.updateLabel()
          // if (this.multiple && this.model.length) {
          //   this.toggleMultipleSelected(true)
          // } else if (!this.multiple && this.model !== '') { // 单选
          //   this.toggleSingleSelected(true)
          // }
        })
      },
      immediate: true
    },
    selectLabel: {
      handler(val) {
        this.$nextTick(() => {
          this.updateLabel()
        })
      },
      immediate: true
    },
    visible(val) {
      if (val) {
        // 如果是远程搜索
        if (this.remote) {
          this.findChild(child => {
            child.selected = this.multiple ? this.model.some(v => String(v) === String(child.value)) : String(this.model) == String(child.value)
          })
          // remote下，设置了默认值，第一次打开时，搜索一次
          const options = this.currentData
          if (!options.length) {
            // 获取远程数据
            this.getRemoteData(this.query)
          }
        } else if (this.currentData.length === 0 && this.lazy) {
          this.loadCodeData()
        }
      }
      // 失焦/聚焦
      if (this.filterable) {
        if (val) {
          this.multiple ? this.$refs.input.focus() : this.$refs.input.select()
        } else {
          // 隐藏下拉框
          this.$refs.input.blur()
          setTimeout(() => {
            this.broadcastQuery('')
          }, 300)
        }
      }
      // 更新下拉框的位置
      this.$nextTick(() => {
        this.broadcast(config.prefix + '-select-dropdown', 'update-popper')
      })
    },
    dropVisible(newValue) {
      // 处理异步加载
      if (this.dropAsync && newValue && !this.dropLoaded) {
        this.dropLoaded = true
      }
      if (newValue) {
        this.$nextTick(() => {
          // 更新下拉框的位置
          this.broadcast(config.prefix + '-select-dropdown', 'update-popper')
          if (this.filterable && !this.remote) {
            let isHidden = true
            this.findChild((child) => {
              if (!child.hidden) {
                isHidden = false
              }
            })
            // 是否能匹配到
            this.notFound = isHidden
          }
        })
      }
    }
  },
  computed: {
    // 下拉框容器的类名
    classes() {
      return [
        `${prefixCls}`,
        `${prefixCls}-selection`,
        {
          [`${prefixCls}-visible`]: this.visible,
          [`${prefixCls}-disabled`]: this.disabled,
          [`${prefixCls}-multiple`]: this.multiple,
          [`${prefixCls}-single`]: !this.multiple,
          [`${prefixCls}-show-clear`]: this.showCloseIcon
        }
      ]
    },
    // 下拉框的类名
    dropdownCls() {
      return [
        this.elementId ? prefixCls + this.elementId : '',
        {
          [prefixCls + '-dropdown-transfer']: this.transfer,
          [prefixCls + '-multiple']: this.multiple && this.transfer
        }
      ]
    },
    // 多选时候动态计算Input框的宽度 keydown触发
    inputStyle() {
      const style = {}
      if (this.multiple) {
        if (this.showPlaceholder) {
          style.width = '100%'
        } else {
          style.width = `${this.inputLength}px`
        }
      }
      if (this.$refs.reference) {
        let maxWidth = Artery.getStyle(this.$refs.reference, "width")
        if (maxWidth.indexOf('px')) {
          maxWidth = `${parseInt(maxWidth.replace('px', '')) - 24}px`
        }
        style.maxWidth = maxWidth
        style.flexGrow = 1
      }
      return style
    },
    dropVisible() {
      let status = true
      let options = this.currentData
      if (!options || options.length == 0) {
        // 解决 http://code.thunisoft.com/artery/artery6/issues/33，静态指定的options不显示问题
        options = this.options
      }
      if (!this.currentLoading && this.remote && this.query === '' && !options.length) {
        status = false
      }
      if (!options.length && !this.remote) {
        status = false
      }
      return this.visible && status
    },
    dropDownListStyles() {
      return {
        overflowY: 'auto',
        maxHeight: `${this.dropDownMaxHeight}px`
      }
    },
    showPlaceholder() {
      let status = false
      status = (this.model === null || this.model === undefined) ?
        true :
        (this.multiple ? !this.model.length : this.model === '')
      return status
    },
    showCloseIcon() {
      return this.clearable && !this.showPlaceholder
    },
    emptyText() {
      if (this.currentLoading) {
        return this.loadingText
      } else if (this.notFoundShow) {
        return this.notFoundText
      } else {
        return ""
      }
    },
    notFoundShow() {
      return (this.notFound && !this.remote) || (this.remote && !this.currentLoading && this.currentData.length == 0)
    },
    selectLabelisExist() {
      return this.selectLabel !== '' && this.selectLabel !== undefined && this.selectLabel !== null && !!this.selectLabel.length
    }
  },
  methods: {
    setModel(value) {
      let _value = (value == null || value == undefined) ? (this.multiple ? [] : '') : value
      if (this.multiple) {
        this.model.push(_value)
      } else {
        this.model = _value
      }
      this.$emit('input', this.model)
      this.handleModelChange(_value)
    },
    spliceModel(index, howmany) {
      this.model.splice(index, howmany)
      this.$emit('input', this.model)
      this.selected.splice(index, howmany)
      this.toggleMultipleSelected()
    },
    handleModelChange(value) {
      this.modelToQuery()
      // model变动，需要重新整合select显示值
      if (this.multiple) {
        this.updateMultipleSelected(value)
      } else {
        this.updateSingleSelected()
      }
    },
    // 设置数据
    setCurrentData(data) {
      let _currentData = data
      if (this.includeOptionRule && Array.isArray(_currentData)) {
        _currentData = _currentData.filter(data => this.includeOptionRule(data))
      }
      // 数据赋值
      this.currentData = _currentData
      if (this.filterable && !this.remote && this.notFound) {
        this.notFound = false
      }
    },
    /**
     * 初始化更新创建实例缓存
     */
    updateOptions() {
      let options = []
      let index = 1

      // 查找子元素
      this.findChild((child) => {
        options.push({
          value: child.value,
          label: (child.label === undefined) ? child.$el.textContent : child.label
        })
        child.index = index++
        // 更新实例列表
        if (!this.optionInstances.includes(child)) {
          this.optionInstances.push(child)
        }
      })
      if (options.length <= 0 && !this.dropLoaded) {
        options = this.currentData.map(function (item) {
          return {
            value: item.code,
            label: item.name
          }
        })
      }
      // options 列表
      this.options = options
      this.updateLabel()
    },
    /**
     * 更新单选选中数据：selected，通过model的改变驱动他的改变
     */
    updateSingleSelected() {
      const type = typeof this.model
      if (type === 'string' || type === 'number') {
        for (let i = 0; i < this.options.length; i++) {
          if (this.model == this.options[i].value) {
            this.selected = this.options[i].label
            break
          }
        }
      }
      this.toggleSingleSelected()
    },
    /**
     * 选中drop中的数据，需要用到options的实例缓存
     */
    toggleSingleSelected(noEmit = false) {
      let value = this.model
      let label = ''
      // 查找所有子组件
      this.findChild((child) => {
        if (String(child.value) === String(value)) {
          // 设置选中状态
          child.selected = true
          // 赋值label
          label = (child.label === undefined) ? child.$el.innerHTML : child.label
        } else {
          // 其他项设置非选中
          child.selected = false
        }
      })
      // 解决 http://code.thunisoft.com/artery/artery-ui/issues/72，加入判断value,length，没有的时候对于数据滞后加载不再hideMenu
      if (!this.remote && value && this.currentData.length == 0) {
        // 隐藏菜单
        this.hideMenu()
      }
      // 非初始化
      const changeValue = this.labelInValue ? {
        value: value,
        label: label
      } : value
      if (!noEmit) {
        /**
         * #event
         *
         * @name    change
         * @param   value   最新的值
         * @param   name    当前传入的props中的name
         * @description   选中的值改变时触发的事件
         * @body
         */
        this.$emit('change', changeValue, this.name)
        this.validate()
      }
      /*
       * 解决 http://code.thunisoft.com/artery/artery-ui/issues/72，加入判断value，没有的时候对于数据滞后加载不再hideMenu
       * 更新下拉框位置
       */
      this.broadcast(config.prefix + '-select-dropdown', 'update-popper')
    },
    /**
     * 清除选择的值
     */
    clearSelect() {
      // 禁用下不可用
      if (this.disabled) {
        return false
      }
      if (this.showCloseIcon) {
        if (this.multiple){
          //删除所有元素
          this.spliceModel(0, this.model.length)
          // 如果可以过滤，那么input获得焦点
          if (this.filterable && this.visible) {
            this.$refs.input.focus()
          }
        }else{
          this.setModel('')
        }
        if (this.filterable) {
          this.query = ''
        }
      }
    },
    /**
     * [内部setModel触发，用于更新显示的label]
     */
    updateMultipleSelected(value) {
      if (Array.isArray(this.model)) {
        let options = this.options.length ? this.options : this.currentData.map(one => {
          return {
            value: one.code,
            label: one.name
          }
        })
        const index = this.selected.map(obj => {
          return obj.value
        }).findIndex(v => v == value)
        // 存在的话就移除
        if (index >= 0) {
          this.selected.splice(index, 1)
        } else {
          // 不存在就添加
          options.forEach(item => {
            if (item.value == value) {
              this.selected.push(item)
              return
            }
          })
        }

      }
      this.toggleMultipleSelected()
    },
    /**
     * 更新多选的下拉选中状态
     */
    toggleMultipleSelected(noEmit = false) {
      let value = this.model
      if (!this.options.length) {
        return
      }
      // 混合值
      const hybridValue = []
      for (let i = 0; i < value.length; i++) {
        hybridValue.push({
          value: value[i]
        })
      }
      this.findChild((child) => {
        const index = value.findIndex(v => String(v) === String(child.value))

        if (index >= 0) {
          child.selected = true
          hybridValue[index].label = (child.label === undefined) ? child.$el.innerHTML : child.label
        } else {
          child.selected = false
        }
      })
      if (!noEmit) {
        this.$emit('change', this.labelInValue ? hybridValue : value)
        this.validate()
      }
      this.broadcast(config.prefix + '-select-dropdown', 'update-popper')
    },
    /**
     * 多选删除标签
     */
    removeTag(index) {
      if (this.disabled) {
        return false
      }
      // this.selected.splice(index, 1)
      this.spliceModel(index, 1)
      // 如果可以过滤，那么input获得焦点
      if (this.filterable && this.visible) {
        this.$refs.input.focus()
      }
      // 更新下拉框位置
      this.broadcast(config.prefix + '-select-dropdown', 'update-popper')
    },
    /**
     * 使用codeType时候加载数据
     */
    loadCodeData() {
      // 加载代码值数据
      if (!this.codeType) {
        return
      }
      // 单值代码映射函数
      const mapper = data => {
        return {
          code: data.code,
          name: data.name,
          levelinfo: data.levelinfo,
          valid: data.valid,
          simpleSpell: data.dmjp
        }
      }
      Artery.loadCode(this.codeType, result => result.map(mapper)).then(data => {
        // 数据赋值
        this.setCurrentData(data)
        this.updateOptions()
        if (this.filterable && !this.dropLoaded) {
          // 可过滤 && 下拉没有渲染
          this.query = this.selected
        }
      })
    },
    /**
     * [外部触发：data、value、loadCode、selectlabel改变]
     * 【用于处理】：
     * 1. data改变 [ 数据改变后需要刷新label，用户可能修改了label]
     * 2. lazy懒加载(selectLabel必须赋值,否则会显示code,因为它无法翻译)
     *  [ 懒加载时候没有data，赋值了selectLabel，此时如果点击删除某个数据，需要用户自己修改v-model，因为传入的label无法对应数据值 ]
     * 【注意】：
     * 必须要有值-this.model，否则就失去了意义
     */
    updateLabel() {
      /**
       * [单/多]遵循逻辑：
       * 1. 如果有Data,就根据model从data里找，如果找不着，说明data错误或者model错误，显示model值，即使懒加载data与model也应该对应
       * 2. 如果没有data，从selectLabel赋值
       * 3. 所以条件都不满足，赋值model
       * 4. 选中
       */
      if (this.multiple) {
        this.updateMultipleLabel()
        this.toggleMultipleSelected(true)
      } else if (!this.multiple) { // 单选
        this.updateSingleLabel()
        this.toggleSingleSelected(true)
      }
    },
    updateMultipleLabel() {
      if (this.remote || !this.model.length) {
        return
      }
      let _data = this.options.length ? this.options : this.currentData.map(one => {
        return {
          value: one.code,
          label: one.name
        }
      })
      if (_data && _data.length > 0) {
        this.selected = this.model.map(one => {
          const _temp = _data.filter(item => item.value == one)
          return {
            value: _temp.length > 0 ? _temp[0].value : one,
            label: _temp.length > 0 ? _temp[0].label : one
          }
        }).filter(one => !!one)
      } else if (this.selectLabelisExist) {
        if (this.model.length !== this.selectLabel.length) {
          console.warn("【ArteryUI提示】:Select组件如果使用了selectLabel，请保证selectLabel与传入的v-model值数据长度以及顺序一致")
          return
        }
        this.selected = this.model.map((item, index) => {
          return {
            value: item,
            label: this.selectLabel[index]
          }
        })
      } else {
        this.selected = this.model.map(one => {
          return {
            value: one,
            label: one
          }
        })
      }
    },
    updateSingleLabel() {
      if (this.remote || this.model === '') {
        return
      }
      if (this.currentData && this.currentData.length > 0) {
        let _tempSelected = null
        const _temp = this.currentData.filter(one => {
          return one.code == this.model
        })
        if (_temp.length > 0) {
          _tempSelected = _temp[0].name
        }
        this.selected = _tempSelected ? _tempSelected : this.model
      } else if (this.selectLabelisExist) {
        this.selected = this.selectLabel
      } else {
        this.selected = this.model
      }
    },
    /************************ 基础方法-START ************************/
    toggleMenu() {
      if (this.disabled) {
        return false
      }
      this.visible = !this.visible
      if (this.visible) {
        /**
         * #event
         *
         * @name    show-menu
         * @body
         * @description     显示下拉选择框触发的事件
         */
        this.$emit('show-menu')
      }
    },
    hideMenu() {
      this.visible = false
      this.focusIndex = 0
      this.broadcast(config.prefix + '-option', 'select-close')
    },
    handleClose() {
      this.hideMenu()
      setTimeout(() => {
        const model = this.model
        if (this.multiple) {
          this.query = '' // handleClose里置空
        } else {
          if (model !== '') {
            this.findChild((child) => {
              if (String(child.value) === String(model)) {
                this.query = child.label === undefined ? child.searchLabel : child.label
              }
            })
          } else {
            this.query = '' // handleClose里置空
          }
        }
        if (this.filterable) {
          this.broadcastQuery('')
        }
      }, 300)
    },
    /************************ 基础方法-END ************************/

    /************************ 键盘操作相关方法-START ************************/
    /**
     * 通过tab和键盘事件的触发
     */
    handleFocus() {
      if (!this.visible) {
        this.toggleMenu()
      }
    },
    handleKeydown(e) {
      const keyCode = e.keyCode
      // tab 键
      if (keyCode == 9) {
        if (document.activeElement != this.$el && this.visible) {
          this.visible = false
        }
      } else {
        // 如果显示
        if (this.visible) {
          // Esc slide-up
          if (keyCode === 27) {
            e.preventDefault()
            this.hideMenu()
          }
          // next
          if (keyCode === 40) {
            e.preventDefault()
            this.navigateOptions('next')
          }
          // prev
          if (keyCode === 38) {
            e.preventDefault()
            this.navigateOptions('prev')
          }
          // enter
          if (keyCode === 13) {
            e.preventDefault()
            this.findChild((child) => {
              if (child.isFocus) {
                // 调用选中的方法
                child.select()
              }
            })
          }
        } else {
          // 如果当前元素获得焦点
          if (document.activeElement == this.$el && keyCode === 13) {
            this.visible = true
          }
        }
      }
    },
    /**
     * 定位当前选中的节点
     */
    navigateOptions(direction) {
      if (direction === 'next') {
        const next = this.focusIndex + 1
        this.focusIndex = (this.focusIndex === this.options.length) ? 1 : next
      } else if (direction === 'prev') {
        const prev = this.focusIndex - 1
        this.focusIndex = (this.focusIndex <= 1) ? this.options.length : prev
      }

      const child_status = {
        disabled: false,
        hidden: false
      }

      let find_deep = false // can next find allowed

      this.findChild((child) => {
        if (child.index === this.focusIndex) {
          child_status.disabled = child.disabled
          child_status.hidden = child.hidden

          if (!child.disabled && !child.hidden) {
            child.isFocus = true
          }
        } else {
          child.isFocus = false
        }

        if (!child.hidden && !child.disabled) {
          find_deep = true
        }
      })

      this.resetScrollTop()

      if ((child_status.disabled || child_status.hidden) && find_deep) {
        this.navigateOptions(direction)
      }
    },
    resetScrollTop() {
      const index = this.focusIndex - 1
      if (!this.optionInstances.length) {
        return
      }
      const bottomOverflowDistance = this.optionInstances[index].$el.getBoundingClientRect().bottom - this.$refs.dropdownUL.getBoundingClientRect().bottom
      const topOverflowDistance = this.optionInstances[index].$el.getBoundingClientRect().top - this.$refs.dropdownUL.getBoundingClientRect().top

      if (bottomOverflowDistance > 0) {
        this.$refs.dropdownUL.scrollTop += bottomOverflowDistance
      }
      if (topOverflowDistance < 0) {
        this.$refs.dropdownUL.scrollTop += topOverflowDistance
      }
    },
    /************************ 键盘操作相关方法-END ************************/
    handleQueryInputClick(){
      // 点击搜索框时，显示树面板
      var flag = !this.visible
      this.visible = this.visible || true
      if (flag && this.visible) {
        this.$emit('show-menu')
      }
    },
    handleQueryChange() {
      // 搜到值没发生改变，直接return，防止重复执行，浪费资源。
      if (this.oldQuery == this.query) {
        return
      }
      if (this.multiple) {
        this.resetInputState()
      }
      this.oldQuery = this.query
      // 只有支持过滤的才需要 query
      if (this.filterable) {
        if (this.remote) {
          /**
           * #event
           *
           * @name    query-change
           * @param   value   当前查询条件的值
           * @param   name    当前传入的props中的name
           * @description   filterable查询参数改变的触发
           * @body
           */
          this.$emit('query-change', this.query, this.name)
          // 获取远程数据
          this.getRemoteData(this.query)
          this.focusIndex = 0
          this.findChild(child => {
            child.isFocus = false
          })
        } else {
          this.$emit('query-change', this.query)
          this.broadcastQuery(this.query)
          // 查询子元素
          this.$nextTick(() => {
            let idHidden = true
            this.findChild((child) => {
              if (!child.hidden) {
                idHidden = false
              }
            })
            // 是否能匹配到
            this.notFound = idHidden
          })
        }
        this.broadcast(config.prefix + '-select-dropdown', 'update-popper')
      }
    },
    /************************ 搜索时候KEYDOWN用-START ************************/
    /**
     * 搜索时候，动态改变input长度
     */
    resetInputState() {
      this.inputLength = this.$refs.input.value.length * 12 + 20
    },
    /**
     * keydown.delete时候删除最后一个标签
     */
    handleInputDelete() {
      //  如果是多选，并且多选的值的长度大于0
      if (this.multiple && this.model.length && this.query === '') {
        this.removeTag(this.model.length - 1)
      }
    },
    /************************ 搜索时候KEYDOWN用-END ************************/

    // model变动，需要查询（非多选 && 支持过滤 && 当前的值！=undefined）
    modelToQuery() {
      if (!this.multiple && this.filterable && this.model !== undefined) {
        let targetChild
        // 找到与model值一样的目标子
        this.findChild((child) => {
          if (String(this.model) === String(child.value)) {
            targetChild = child
          }
        })
        if (targetChild) {
          this.query = targetChild.label ? targetChild.label : (targetChild.searchLabel ? targetChild.searchLabel : targetChild.value)
        } else {
          this.query = ''
        }
      }
    },
    // query 变动导致的事件广播
    broadcastQuery(val) {
      // 查找子组件option-group，如果存在，那么说明需要分发事件到 option-group 中，默认为 option中
      if (Artery.findComponentDownward(this, config.prefix + '-option-group')) {
        this.broadcast(config.prefix + '-option-group', 'query-change', val)
        this.broadcast(config.prefix + '-option', 'query-change', val)
      } else {
        this.broadcast(config.prefix + '-option', 'query-change', val)
      }
    },
    /**
     * 下拉的菜单dom改变
     */
    slotChange() {
      this.options = []
      this.optionInstances = []
    },
    debouncedAppendRemove() {
      // 防抖动函数
      return debounce(function () {
        // 非远程
        if (!this.remote) {
          this.modelToQuery()
          this.$nextTick(() => this.broadcastQuery(''))
        } else {
          this.findChild((child) => {
            child.updateSearchLabel() // #1865
            child.selected = this.multiple ? this.model.some(v => String(v) === String(child.value)) : String(this.model) === String(child.value)
          })
        }
        // this.visible=false;
        this.slotChange()
        this.updateOptions()
      })
    },
    adjustLoading(flag, delay = 0) {
      if (!flag) {
        setTimeout(() => {
          // 正在加载数据
          this.currentLoading = flag
        }, delay)
      } else {
        // 正在加载数据
        this.currentLoading = flag
      }
    },
    doGetRemoteData: function (query) {
      // 先判断用户是否自己写了请求数据的方法
      if (Artery.type(this.remoteMethod) === 'function') {
        // 正在加载
        this.adjustLoading(true)
        this.remoteMethod(query, (newData) => {
          this.setCurrentData(newData)
          // 加载数据完毕 （隐藏加载）
          this.adjustLoading(false)
          this.broadcast(config.prefix + '-select-dropdown', 'update-popper')
        })
      }
    },
    // 获取远程数据
    getRemoteData: function (query) {
      var _this = this
      // 性能优化
      if (_this.timerRemote) {
        clearTimeout(_this.timerRemote)
      }
      if (this.filterable) {
        // 防抖函数
        _this.timerRemote = setTimeout(function () {
          _this.doGetRemoteData(query)
        }, _this.delay)
      } else {
        _this.doGetRemoteData('')
      }
    },
    // 绑定事件
    bindEvent() {
      // 绑定keydown
      document.addEventListener('keydown', this.handleKeydown)
      //  option 添加 （mounted）
      this.$on('append', this.debouncedAppendRemove())
      // option 移除 （beforeDestory）
      this.$on('remove', this.debouncedAppendRemove())
      // option  select
      this.$on('select-selected', (value) => {
        // 这里是为了兼容部分旧系统单值代码和系统值类型不统一导致
        if (!this.multiple && String(this.model) === String(value)) {
          this.hideMenu()
        } else {
          // 多选
          if (this.multiple) {
            const index = this.model.findIndex(v => v == value)
            // 存在的话就移除
            if (index >= 0) {
              this.removeTag(index)
            } else {
              // 不存在就添加
              this.setModel(value)
              this.broadcast(config.prefix + '-select-dropdown', 'update-popper')
            }
            // 如果是过滤
            if (this.filterable) {
              this.query = ''
              // 获得焦点
              this.$refs.input.focus()
            }
          } else {
            this.setModel(value)
            this.$nextTick(() => {
              // 放到里面, 不然filterable的情况, 选中会一闪
              this.hideMenu()
              this.findChild((child) => {
                if (String(child.value) === String(value)) {
                  child.selected = true
                } else {
                  // 设置选中状态
                  child.selected = false
                }
              })
            })
          }
        }
      })
    },
    // 解除事件
    unbindEvent() {
      // 解除 keydown 事件
      document.removeEventListener('keydown', this.handleKeydown)
      //  option 添加 （mounted）
      this.$off('append')
      // option 移除 （beforeDestory）
      this.$off('remove')
      // option  select
      this.$off('select-selected')
    },
    // 找组件
    findChild(cb) {
      const find = function (child) {
        const name = child.$options.componentName
        if (name) {
          cb(child)
        } else if (child.$children.length) {
          child.$children.forEach((innerChild) => {
            // 递归查找
            find(innerChild, cb)
          })
        }
      }
      // 优先从实例列表中查找
      if (this.optionInstances.length) {
        this.optionInstances.forEach((child) => {
          // 查找子组件
          find(child)
        })
      } else {
        this.$children.forEach((child) => {
          find(child)
        })
      }
    },
    // 获取待校验value
    getValidateValue: function () {
      return this.model === undefined ? null : this.model
    },
    // 重置为初始值
    reset() {
      if (this.disabled) {
        return false
      }
      this.model = this._originalValue
      this.$emit('input', this.model)
      this.$emit('change')
      this.$nextTick(() => {
        this.modelToQuery()
        this.updateLabel()
      })
    },
  },
  beforeMount() {
    // 1.非懒加载
    // 2.懒加载,有初始化值,没有设置对应的翻译
    if (!this.lazy || (this.value && !this.selectLabel)) {
      this.loadCodeData()
    }
    // 初始化函数
    this.modelToQuery()
    this.$nextTick(() => {
      this.broadcastQuery('')
      if (!this.remote) {
        this.updateOptions()
        // 处理 remote 初始值
      }
      // 绑定事件
      this.bindEvent()
    })
    // 保存初始值，用于reset
    this._originalValue = Artery.deepCopy(this.checked)
  },
  beforeDestroy() {
    // 解除事件
    this.unbindEvent()
  }
}
